#include <QtGui>
#include <QPainter>
#include <QDebug>

#include "graphwidget.h"

GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent)
{
    data = 0;
    dataMin = dataMax = 0;
    lo = hi = 0;

    graphX = leftBorder;
    graphY = topBorder;
}

inline int GraphWidget::toVertPos(double y)
{
    return (int)(graphHeight * (1 - (dataMax - y) /
                                (dataMax - dataMin)));
}

void GraphWidget::paintLegend(QPainter *painter)
{
    painter->drawText(0, topBorder - 5, tr("aaa"));
    painter->drawText(0, topBorder + graphHeight - 5, tr("bbb"));
}

#define V(x) (topBorder + (x))
#define H(x) (leftBorder + (x))

void GraphWidget::paintGraph(QPainter *painter)
{
    qDebug() << "painting graph";

    painter->save();
    painter->drawRect(graphX - 1, graphY - 1, graphWidth + 1, graphHeight + 1);
    painter->setClipRect(graphX - 1, graphY - 1, graphWidth + 1, graphHeight + 1);
    painter->save();

    int oh, ol;

    qDebug() << graphWidth;
    for(int i = 0; i < graphWidth; i++) {
        int h = toVertPos(hi[i]);
        int l = toVertPos(lo[i]);
        if(l > oh && i) {
            painter->drawLine(H(i-1), V(oh), H(i), V(l));
        } else if(h < ol && i) {
            painter->drawLine(H(i-1), V(ol), H(i), V(h));
        }
        painter->drawLine(H(i), V(l),
                         H(i), V(h));
        oh = h; ol = l;
    }

}

void GraphWidget::paintEvent(QPaintEvent */*event*/)
{
    QPainter painter(this);

    if(!data) return;

    graphWidth = width() - leftBorder - rightBorder;
    graphHeight = height() - topBorder - bottomBorder;

    recalc();
    paintLegend(&painter);
    paintGraph(&painter);
}

void GraphWidget::recalc()
{
    if(!data) return;

    double min, max;

    int inPos = 0;
    int outPos = 0;

    if(hi) delete [] hi;
    if(lo) delete [] lo;

    lo = new double[graphWidth + 1];
    hi = new double[graphWidth + 1];

    double ratio = (double)(displayEnd - displayStart) / (double)graphWidth;

    min = max = lo[0] = hi[0] = data[0];

    for (;;) {
        if (outPos >= graphWidth || inPos >= displayEnd - displayStart) break;

        if (data[displayStart + inPos] > hi[outPos]) {
            hi[outPos] = data[displayStart + inPos];
            if (data[displayStart + inPos] > max) {
                max = data[displayStart + inPos];
            }
        }

        if (data[displayStart + inPos] < lo[outPos]) {
            lo[outPos] = data[displayStart + inPos];
            if (data[displayStart + inPos] < min) {
                min = data[displayStart + inPos];
            }
        }

        if ((inPos + 1) < ratio * (outPos + 1)) {
            inPos++;
            if ((double)inPos - 0.5 > ratio * outPos) {
                outPos++;
                goto outInit;
            }
        } else {
            outPos++;
            if (((double)outPos - 0.5) * ratio > inPos) {
                inPos++;
            }
            goto outInit;
        }
        continue;

        outInit:
        lo[outPos] = hi[outPos] = data[displayStart + inPos];
    }

    qDebug() << "out pos:" << outPos;

    // If it is initial display of data, we must calculate min and max
    if (displayStart == 0 && displayEnd == dataLen) {
        dataMin = min;
        dataMax = max;
    }
}

void GraphWidget::setData(double *data, int len)
{
    this->data = data;
    this->dataLen = len;

    qDebug() << "got" << len << "data";

    reset();
}

void GraphWidget::reset()
{
    if(!data) return;

    displayStart = 0;
    displayEnd = dataLen;

    repaint();
}

#define MIN(a, b) (((a) < (b))?(a):(b))

void GraphWidget::left()
{
    if(!data) return;
    if(displayStart <= 0) return;

    int shift = MIN((displayEnd - displayStart) / 3, displayStart);

    displayStart -= shift;
    displayEnd -= shift;

    repaint();
}

void GraphWidget::right()
{
    if(!data) return;
    if(displayEnd >= dataLen) return;

    int shift = MIN((displayEnd - displayStart) / 3, dataLen - displayEnd);

    displayStart += shift;
    displayEnd += shift;

    repaint();
}

void GraphWidget::zoomIn()
{
    if(!data) return;
    if(displayEnd - displayStart < 20) return;

    int delta = (displayEnd - displayStart) / 4;

    displayStart += delta;
    displayEnd -= delta;

    repaint();
}

void GraphWidget::zoomOut()
{
    if(!data) return;
    int delta = (displayEnd - displayStart) / 2;
    displayStart -= delta;
    displayEnd += delta;

    if(displayStart < 0) {
        displayEnd -= displayStart;
        displayStart = 0;
    }

    if(displayEnd > dataLen) {
        displayStart -= (displayEnd - dataLen);
        displayEnd = dataLen;
    }

    if(displayStart < 0) {
        displayStart = 0;
    }

    repaint();
}
